package com.markby.fscm.cryptogram.uitls;

import com.markby.fscm.constant.Constants;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;

import java.security.*;

/**
 * @program: keystore
 * @description: SM2工具类
 * @author: Markby
 * @create: 2024-07-27 17:55
 **/
public class SM2Utils extends GMBaseUtil {

    /**
     * 生成SM2密钥对
     *
     * @return 密钥对
     * @throws NoSuchAlgorithmException           算法异常
     * @throws NoSuchProviderException            提供者异常
     * @throws InvalidAlgorithmParameterException 算法参数异常
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Constants.EC, BC);
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(GMObjectIdentifiers.sm2p256v1.getId());
        keyPairGenerator.initialize(ecSpec, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * sm2公钥加密
     *
     * @param publicKey     加密公钥
     * @param dataToEncrypt 待加密数据
     * @param mode          加密模式 SM2Engine.Mode.C1C2C3/C1C3C2
     * @return 加密后的数据
     * @throws InvalidCipherTextException 加密异常
     * @throws InvalidKeyException        密钥异常
     */
    public static byte[] encryptWithSM2Engine(BCECPublicKey publicKey, byte[] dataToEncrypt, SM2Engine.Mode mode) throws InvalidCipherTextException, InvalidKeyException {
        SM2Engine sm2Engine = new SM2Engine(mode);
        BCECPublicKey bcecPublicKey = publicKey;
        ECPublicKeyParameters ecPublicKeyParameters = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(bcecPublicKey);
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters));
        return sm2Engine.processBlock(dataToEncrypt, 0, dataToEncrypt.length);
    }

    /**
     * sm2私钥解密
     *
     * @param privateKey    私钥
     * @param dataToDecrypt 待解密数据
     * @param mode          加密模式 SM2Engine.Mode.C1C2C3/C1C3C2
     * @return 加密后的数据
     * @throws InvalidCipherTextException 加密异常
     */
    public static byte[] decryptWithSM2Engine(BCECPrivateKey privateKey, byte[] dataToDecrypt, SM2Engine.Mode mode) throws InvalidCipherTextException, InvalidKeyException {
        SM2Engine sm2Engine = new SM2Engine(mode);
//        ECPrivateKeyParameters ecPrivateKeyParameters = privateKey.engineGetKeyParameters();
        ECPrivateKeyParameters ecPrivateKeyParameters = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
        sm2Engine.init(false, ecPrivateKeyParameters);
        return sm2Engine.processBlock(dataToDecrypt, 0, dataToDecrypt.length);
    }

    /**
     * 私钥签名
     */
    public static byte[] signByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BC);
        sig.initSign(privateKey);
        sig.update(data);
        return sig.sign();
    }

    /**
     * 公钥验签
     */
    public static boolean verifyByPublicKey(byte[] data, PublicKey publicKey, byte[] signature) throws Exception {
        Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BC);
        sig.initVerify(publicKey);
        sig.update(data);
        return sig.verify(signature);
    }


}
